/**
 * @project heima-leadnews
 * @author MECHREVO
 * @date 2024/1/21 09:41:11
 * @version 1.0
 */
package com.heima.schedule.service.Impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.redis.CacheService;
import com.heima.model.schedule.dto.TaskDto;
import com.heima.model.schedule.pojo.Taskinfo;
import com.heima.model.schedule.pojo.TaskinfoLogs;
import com.heima.schedule.mapper.TaskinfoLogsMapper;
import com.heima.schedule.mapper.TaskinfoMapper;
import com.heima.schedule.service.TaskInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
@Transactional
@Slf4j
public class TaskInfoServiceImpl extends ServiceImpl<TaskinfoMapper, Taskinfo> implements TaskInfoService {
    /**
     * 添加任务
     *
     * @param taskDto
     */
    @Autowired
    private TaskinfoLogsMapper taskinfoLogsMapper;
    @Autowired
    private CacheService cacheService;
    @Override
    public void addTask(TaskDto taskDto) {
        //1.添加任务到DB
        addTaskToDB(taskDto);

        //2.根据任务时间，选择将任务添加到redis
        addTaskRedis(taskDto);
    }
    //2.根据任务时间，选择将任务添加到redis
    private void addTaskRedis(TaskDto taskDto) {
        //拿到当前时间
        long now = System.currentTimeMillis();
        //预设时间。当前时间往后延迟5分钟的时间
        long futureTime = now + (1000 * 60 * 5);
        //任务执行时间
        long executeTime = taskDto.getExecuteTime();
        // 2.根据任务执行时间判断当前任务是否存入redis
        if(executeTime<=now){
            //存入List中
           String key= "topic_"+taskDto.getTaskType()+"_"+taskDto.getPriority();
           cacheService.lLeftPush(key, JSON.toJSONString(taskDto));
        }else if(executeTime<=futureTime){
            //存入zSet中
            String key= "future_"+taskDto.getTaskType()+"_"+taskDto.getPriority();
            cacheService.zAdd(key, JSON.toJSONString(taskDto),taskDto.getExecuteTime());
        }
    }

    //1.添加任务到DB
    private void addTaskToDB(TaskDto taskDto) {
        //1.将数据保存到taskinfo
        Taskinfo taskinfo = new Taskinfo();
        BeanUtils.copyProperties(taskDto,taskinfo);
        taskinfo.setExecuteTime(new Date(taskDto.getExecuteTime()));
        save(taskinfo);

        taskDto.setTaskId(taskinfo.getTaskId());
        //2.将任务进行日志的保存
        TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
        BeanUtils.copyProperties(taskinfo,taskinfoLogs);
       taskinfoLogs.setVersion(1);
       taskinfoLogs.setStatus(0);
       taskinfoLogsMapper.insert(taskinfoLogs);
    }

    /**
     * 拉去任务
     *
     * @param taskType
     * @param priority
     * @return
     */
    @Override
    public TaskDto pullTask(Integer taskType, Integer priority) {
        //1.根据任务类型和任务优先级来拼接redis对应的list的key
        String key= "topic_"+taskType+"_"+priority;
        //2.根据key将任务从redis的list中弹出
        String taskDtoJSon = cacheService.lRightPop(key);
        TaskDto taskDto = JSON.parseObject(taskDtoJSon, TaskDto.class);
        //3.将任务从DB的taskinfo中移除
        if(taskDto!=null) {
            removeById(taskDto.getTaskId());
            //4.将taskinfoLogs中状态修改为1
            TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
            taskinfoLogs.setTaskId(taskDto.getTaskId());
            taskinfoLogs.setStatus(1);
            taskinfoLogsMapper.updateById(taskinfoLogs);
        }
        return taskDto;
    }

    /**
     * 定时刷新ZSet数据到List中
     * 执行频次：每分钟执行一次
     */
    @Scheduled(cron = " 0 0/1 * * * ? ")
    public void refreshZSetToList(){
        log.info("===定时刷新ZSet数据到listzhong=====");
        Set<String> zsetKeys = cacheService.scan("future_*");
        for (String zsetKey : zsetKeys) {
            String[] split = zsetKey.split("future_");
            String listKey = "topic_" + split[1];

            //将ZSet中任务的执行时间（分数）《=当前时间的数据取出来
            Set<String> taskDto = cacheService.zRangeByScore(zsetKey, 0, System.currentTimeMillis());
            cacheService.refreshWithPipeline(zsetKey,listKey,taskDto);
        }
    }

    /**
     * 定时刷新数据库到redis执行频次：每五分钟执行一次
     */
    @Scheduled(cron = " 0 0/5 * * * ? ")
    public void refreshDbToRedis(){
        log.info("=====定时刷新Db数据到redis中======");
        //将数据库中5分钟内要执行的任务数据查出来
        //5分钟内的数据
        Date futureTime = new Date(System.currentTimeMillis() + (1000 * 60 * 5));
        //当前系统时间任务执行时间《=预设时间：开设ZSet
        List<Taskinfo> taskinfos = list(Wrappers.lambdaQuery(Taskinfo.class)
                .le(Taskinfo::getExecuteTime, futureTime));
        if(taskinfos!=null&&taskinfos.size()>0){
            log.info("=====开启刷新DB数据到redis中======");
            //存入list中，注意：存之前一定要清除原有数据
            Set<String> listKeys = cacheService.scan("topic_*");
            cacheService.delete(listKeys);
            Set<String> zsetKeys = cacheService.scan("future_*");
            cacheService.delete(zsetKeys);
        }
        for (Taskinfo taskinfo : taskinfos) {
            TaskDto taskDto = new TaskDto();
            BeanUtils.copyProperties(taskinfo,taskDto);
            taskDto.setExecuteTime(taskinfo.getExecuteTime().getTime());
            addTaskRedis(taskDto);
        }
    }
}
